import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Properties;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.zest.core.widgets.Graph;
import org.eclipse.zest.core.widgets.GraphConnection;
import org.eclipse.zest.core.widgets.GraphItem;
import org.eclipse.zest.core.widgets.GraphNode;
import org.eclipse.zest.core.widgets.ZestStyles;

/**
 * 
 * Contains methods to add, delete, and modify nodes and connections.
 *
 */
public class NodeUtils {
	
	private Graph g;
	private NodeColorUtils colorUtils;
	
	ArrayList<GraphNode> selection = new ArrayList<GraphNode>();
	
	/**
	 * Contains methods to add, delete, and modify nodes and connections.
	 * @param d
	 * @param g
	 */
	public NodeUtils(Display d, Graph g)
	{
		this.g = g;
		
		colorUtils = new NodeColorUtils(d);
		
		Properties props = new Properties();
		FileInputStream fis;
		
		try
		{
			fis = new FileInputStream("MapEditor.properties");
			props.load(fis);
		}
		catch(FileNotFoundException e)
		{
			e.printStackTrace();
			return;
		}
		catch(IOException e)
		{
			e.printStackTrace();
			return;
		}
		
		try
		{
			colorUtils.setImage(NodeColorUtils.NodeColor.Violet, props, "violetImagePath");
			colorUtils.setImage(NodeColorUtils.NodeColor.Blue, props, "blueImagePath");
			colorUtils.setImage(NodeColorUtils.NodeColor.Green, props, "greenImagePath");
			colorUtils.setImage(NodeColorUtils.NodeColor.Yellow, props, "yellowImagePath");
			colorUtils.setImage(NodeColorUtils.NodeColor.Orange, props, "orangeImagePath");
			colorUtils.setImage(NodeColorUtils.NodeColor.Red, props, "redImagePath");
		}
		catch(NoSuchElementException e)
		{
			e.printStackTrace();
			return;
		}
	}
	
	/**
	 * Add a new node to the graph
	 * @param text - node name
	 * @param x - x coord on screen
	 * @param y - y coord on screen
	 * @param nodeColor - NodeColor enumerated type
	 * @return new GraphNode
	 */
	public GraphNode createNode(String text, int x, int y, NodeColorUtils.NodeColor nodeColor)
	{
		GraphNode newNode = new GraphNode(g, ZestStyles.NODES_HIDE_TEXT | ZestStyles.NODES_FISHEYE, text);
		newNode.setLocation(x, y);

		newNode.setData(nodeColor);
		newNode.setImage(colorUtils.getImage(nodeColor.getIdx()));
		
		return newNode;
	}
	
	/**
	 * Removes all nodes and connections in the specified list
	 * @param list of GraphItems
	 */
	public void deleteNodes(List<GraphItem> list)
	{
		ArrayList<GraphItem> deletedList = new ArrayList<GraphItem>();

		for (GraphItem gi : list)
			deletedList.add(gi);

		// Zest workaround: Must deselect everything or bad stuff can happen.
		g.setSelection(null);
		
		for (GraphItem gi : deletedList)
			if (!gi.isDisposed())
				gi.dispose();		
	}
	
	/**
	 * Store the selected nodes so we can add connections between this list and a new selection
	 * @param newList - list of GraphIte,s
	 */
	public void saveSelection(List<GraphItem> newList)
	{
		selection.clear();
		for (GraphItem gi : newList)
		{
			if (gi.getItemType() == GraphItem.NODE)
				selection.add((GraphNode) gi);
		}
	}

	/**
	 * Add a connection between two nodes
	 * @param source
	 * @param destination
	 */
	public void newConnection(GraphNode source, GraphNode destination)
	{
		GraphConnection gc = new GraphConnection(g, SWT.NONE, source, destination);
		gc.setLineWidth(2);
	}

	/**
	 * Create a connection between the current groups of selected nodes and whatever was selected previously, avoiding making duplicate connections.
	 * 
	 */
	@SuppressWarnings("unchecked")
	public void makeConnection()
	{
		boolean duplicate;

		// gi1 walks through the currently-selected nodes in the graph
		for (GraphItem gi1 : (List<GraphItem>) g.getSelection())
		{
			if (gi1.getItemType() == GraphItem.NODE)
			{
				GraphNode gn1 = (GraphNode) gi1;
				duplicate = false;
				
				// TODO Define MAXCONN as the maximum number of connections a node may have.
				// TODO If gi1 or gi2 has more than (MAXCONN - 1) connections, fail gracefully and throw a "Cannot connect these nodes. <gi[12]> has too many connections: max is <MAXCONN>." warning.
				
				// TODO If source and destination node are the same color, fail gracefully and throw a "Cannot connect two same-colored nodes." warning.
				
				// gi2 walks through the previous selection and adds connections to gi1.
				for (GraphNode gi2 : selection)
				{
					for (GraphConnection gc : (List<GraphConnection>) gn1.getTargetConnections())
						if (gi2 == gc.getSource() || gi2 == gc.getDestination())
						{
							duplicate = true;
							break;
						}
					
					for (GraphConnection gc : (List<GraphConnection>) gn1.getSourceConnections())
						if (gi2 == gc.getSource() || gi2 == gc.getDestination())
						{
							duplicate = true;
							break;
						}
					
					if (!duplicate)
						newConnection(gn1, gi2);
				}
			}
	    }
	}
	
	/**
	 * See NodeColorUtils.getColorString(int idx)
	 * @param idx
	 * @return
	 */
	public String getColorString(int idx)
	{
		return colorUtils.getColorString(idx);
	}

	/**
	 * See NodeColorUtils.colorLookup(int colorInt)
	 * @param colorInt
	 * @return
	 */
	public NodeColorUtils.NodeColor colorLookup(int colorInt)
	{
		return colorUtils.colorLookup(colorInt);
	}
	
	/**
	 * See NodeColorUtils.colorLookup(String colorStr)
	 * @param colorStr
	 * @return
	 */
	public NodeColorUtils.NodeColor colorLookup(String colorStr)
	{
		return colorUtils.colorLookup(colorStr);
	}
	
	/**
	 * See NodeColorUtils.getImage(int idx)
	 * @param idx
	 * @return
	 */
	public Image getImage(int idx)
	{
		return colorUtils.getImage(idx);
	}
}
